import { Quaternion, Spherical, Texture, Vector2, Vector3 } from "three";
import GeoUtils from "./GeoUtils";
import { Point } from "../Engine";
import Const from "../application/constants/Const";
import { LRUMap } from "lru_map";
export default class Helper {
  /** 数组删除对象 */
  public static listSplice(list: Array<any>, obj: any) {
    if (obj instanceof Function) {
      list.forEach((item, idx) => {
        if (item.toString() == obj.toString()) list.splice(idx, 1);
      });
    } else {
      let index = list.indexOf(obj);
      if (index >= 0)
        list.splice(index, 1);
    }
  }
  /** 去空 */
  public static trim(str: string) {
    return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  }
  /** 分割字符 */
  public static splitWords(str: string) {
    return this.trim(str).split(/\s+/);
  }
  /** 开头计算 */
  public static startWith(str: string, w: string) {
    let reg = new RegExp("^" + w);
    return reg.test(str);
  }
  /** 结束计算 */
  public static endWith(str: string, w: string) {
    let reg = new RegExp(w + "$");
    return reg.test(str);
  }
  /** 判断是否为空 */
  public static isEmpty(str: string): boolean {
    if (str == null || str.length == 0 || str == undefined)
      return true;
    return false;
  }
  /**获取地址栏传递的参数 */
  public static getQueryString(name: string): string {
    let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
    let r = window.location.search.substr(1).match(reg);
    if (r != null) return unescape(r[2]); return "";
  }
  /** 判断返回数据是否成功 */
  public static isSuccess(data: { success: boolean, message: string }): boolean {
    if (!data.success) {
      console.log(data.message);
      return false;
    }
    return true;
  }
  /**对深度值做偏移，对顶点值并没有改动。深度偏移的公式为：depth=depth+(polygonOffsetFactor × slope + polygonOffsetUnits) */
  public static enablePolygonOffset(material: any, zoom: number, zIndex: any) {
    if (material) {
      material.polygonOffset = true;
      material.polygonOffsetFactor = zoom;
      material.polygonOffsetUnits = -zIndex * zoom;
    }
  }
  /**禁用多边形偏移值 */
  public static disEnablePolygonOffset(material: any) {
    if (material) {
      material.polygonOffset = false;
      material.polygonOffsetFactor = 0;
      material.polygonOffsetUnits = 0;
    }
  }
  /** 判断是否是PC */
  public static isPC(): boolean {
    let userAgentInfo = navigator.userAgent;
    let Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    let flag = true;
    for (let v = 0; v < Agents.length; v++) {
      if (userAgentInfo.indexOf(Agents[v]) > 0) {
        flag = false;
        break;
      }
    }
    return flag;
  }
  /** 阻止鼠標冒泡 */
  public static stopEvents(e: MouseEvent): void {
    if (e.preventDefault)
      e.preventDefault();
    if (e.stopPropagation)
      e.stopPropagation();
  }
  /** 提示 */
  public static warn(text: string) {
    let div = document.createElement('div');
    div.innerHTML = text;
    document.body.appendChild(div);
    div.style.cssText = `
        pointer-events: none;
        position: absolute;
        top: -100px;
        background: rgba(39, 50, 56, .8);
        color: rgb(255, 255, 255);
        padding: 10px 40px;
        letter-spacing: 1px;
        font-size: 15px;
        border-radius: 5px;
        box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        z-index:999;
        `;
    div.style.left = `calc(50vw - ${div.offsetWidth / 2}px)`;
    div.style.transition = "top 250ms ease-out";
    div.style.top = "20px";
    window.setTimeout(() => {
      document.body.removeChild(div);
    }, 3000);
  }

  public static getTileGrids(num: number) {
    let grids = [];
    for (let curCircle = 0; curCircle <= num; curCircle++) {
      if (curCircle == 0) {
        grids.push([0, 0]);
      } else {
        let rightCol = curCircle;
        let leftCol = -curCircle;
        let downRow = 0;
        for (let j = 0; j <= curCircle; j++) {
          downRow = j;
          grids.push([downRow, rightCol]);
        }
        for (let j = 0; j < curCircle * 2; j++) {
          grids.push([downRow, (rightCol - j - 1)]);
        }
        let leftUpRow = 0;
        for (let j = 1; j < 2 * curCircle + 1; j++) {
          if (j < curCircle) {
            grids.push([(curCircle - j), leftCol]);
          } else {
            leftUpRow = (0 - (j - curCircle));
            grids.push([leftUpRow, leftCol]);
          }
        }
        for (let j = 1; j < curCircle * 2; j++) {
          grids.push([leftUpRow, (leftCol + j)]);
        }
        for (let j = curCircle; j > 0; j--) {
          grids.push([(0 - j), rightCol]);
        }
      }
    }
    return grids;
  }

  /**判断输入的值是否为数字 */
  public static isNumber(inputData) {
    if (parseFloat(inputData).toString() == "NaN") {
      return false;
    } else {
      return true;
    }
  }

  /**
   * 错误数值使用默认值
   * @param num
   * @param def
   */
  public static getDefaultNumber(num: number, def: number): number {
    if (num == Number.NaN || num == null || num == undefined)
      return def;
    return num;
  }

  /**
   * 错误字符串使用默认值
   * @param str
   * @param def
   */
  public static getDefaultString(str: string, def: string): string {
    if (str == undefined || str == null || str.length == 0)
      return def;
    return str;
  }



  private static timerSet = {};
  /** 防抖函数 */
  public static debounce(method: Function, scope: any, delay: number, uuid: string) {
    if (this.timerSet[uuid]) clearTimeout(this.timerSet[uuid]);
    this.timerSet[uuid] = setTimeout(() => {
      method.apply(scope);
    }, delay);
  }

  /** 进阶防抖函数 */
  public static debounce2(func, wait, immediate?) {
    let timeout, args, context, timestamp, result, later = function () {
      let last = new Date().getTime() - timestamp;
      if (last < wait && last >= 0) timeout = setTimeout(later, wait - last); else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          timeout || (context = args = null);
        }
      }
    };
    return function () {
      context = this;
      args = arguments;
      timestamp = new Date().getTime();
      let callNow = immediate && !timeout;
      timeout || (timeout = setTimeout(later, wait));
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }
      return result;
    };
  }

  private static checkImageExists(url, callback) {
    const xhr = new XMLHttpRequest();
    xhr.timeout = 2000; // 2秒超时
    xhr.open('HEAD', url); // 仅获取响应头
    xhr.onload = () => callback(xhr.status >= 200 && xhr.status < 300);
    xhr.onerror = () => callback(false);
    xhr.send();
}

  private static getXmlHttp(url: string, texture, fn?: Function, isCache?: boolean){
     // 使用XMLHttpRequest请求图片
      let xhr = new XMLHttpRequest();
      xhr.timeout = 5000; // 5秒超时
      xhr.open('GET', url, true);
      //  xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
      xhr.responseType = 'arraybuffer';
      xhr.onloadend = function (ev: ProgressEvent) {
        // let isSuccess = (ev.loaded / ev.total) == 1;
        let isSuccess = (xhr.status >= 200 && xhr.status < 300);
        if (isSuccess) {
          // 将获取到的数据设置到纹理对象A中
          let blob = new Blob([xhr.response]);

          // 使用URL.createObjectURL创建图片URL
          let imageUrl = URL.createObjectURL(blob);
          let image = new Image();
          image.src = imageUrl;
          image.onload = function () {
            texture.image = image;// blob; 
            // 通过纹理对象的needsUpdate属性告知Three.js需要更新纹理
            texture.needsUpdate = true;

            if (fn instanceof Function) {
              fn(true, texture);
            }
            Helper.freeImageNet(xhr, texture);

            if (isCache === true) {
              Helper.imgCache.set(url, image);
            }
          }
        } else {
          if (fn instanceof Function) {
            fn(false, undefined);
          }
          Helper.freeImageNet(xhr, texture);
        }
      }
      xhr.ontimeout = xhr.onerror = xhr.onabort = function(){
        if (fn instanceof Function) {
          fn(false, undefined);
        }
        Helper.freeImageNet(xhr, texture);
      }

      xhr.send();
      
      return xhr;
  }

  private static freeImageNet(xhr, texture){
       xhr.abort();
       xhr.onload = null; // 解除引用
       xhr = null;
       texture['imageLoader'] = null;
  }

  public static imgCache:LRUMap<string, any> = new LRUMap(100);
  /** 创建纹理 */
  public static createTexture(url: string, fn?: Function, isCache?: boolean) {
    let texture = new Texture();
    if (isCache && Helper.imgCache.has(url)) {
      texture.image = Helper.imgCache.get(url);
      texture.needsUpdate = true;
      if (fn instanceof Function) fn(true, texture);
    } else {
      // Helper.checkImageExists(url, function(isExist){
      //   if(isExist){
            let xhr = Helper.getXmlHttp(url, texture, fn, isCache);
            texture['imageLoader'] = xhr;
        }
      // });
    // }
    return texture;
  }
  /* 创建纹理 */
  public static createTextureWithData(imageData: string, fn?: Function) {
    let texture = new Texture();
      let image = new Image();
      image.crossOrigin = "*";
      image.onload = function () {
        texture.image = image;
        texture.needsUpdate = true;
        if (fn) {
          fn(true, texture);
        }
        texture['imageLoader'] = null;
      };
      image.onerror = function () {
        if (fn) {
          fn(false);
        }
        texture['imageLoader'] = null;
      };
      image.src = imageData;
      texture['imageLoader'] = image;
    
     return texture;
  }
  /** 删除图片缓存 */
  public static removeCache(url: string) {
    if (Helper.imgCache.has(url)) {
       this.imgCache.delete(url);
    }
  }


  public static isEffectMesh(mesh) {
    if (mesh.userData && mesh.userData.isEffectMesh) {
      return true;
    }

    return false;
  }
  /** 加载网络文件*/
  public static fileCache = {};

  public static loadFile(url: string, onLoad: Function, onError: Function, cache: boolean) {

    if (cache && this.fileCache[url]) {
      if (onLoad) onLoad(this.fileCache[url]);
    } else {
      let xhr = new XMLHttpRequest();
      if (true)
        xhr.responseType = "arraybuffer";
      xhr.open('GET', url, true);
      let response = null;
      xhr.onload = function (event: any) {
        response = this.response;

        if (onLoad) onLoad(response);

        xhr.onload = null;
      };
      xhr.onerror = function (event: any) {
        if (onError) onError(event);
      };

      if (cache) this.fileCache[url] = response;
      xhr.send();
    }
  }

  /**
   * 获取字符串的显示器长度
   * @param str 字符串
   * @returns 
   */
  public static getMonitorLength(str) {
    let count = 0;
    for (var i = 0; i < str.length; i++) {  //遍历字符串，枚举每个字符
      if (str.charCodeAt(i) > 255) {  //字符编码大于255，说明是双字节字符(即是中文)
        count += 3;  //汉字占3位
      } else {
        count += 2;  //英文和标点符号占两位
      }
    }

    return count;
  }

  public static jsCache = {};
  public static loadScript(url: string | Array<string>, callback) {
    if (url instanceof Array) {
      let u = url.shift();
      if (u === undefined) {
        if (callback instanceof Function) {
          callback();
        }
      } else {
        this.loadScript(u, () => { this.loadScript(url, callback) });
      }
    } else {
      if (this.jsCache[url.toString()]) {
        if (callback instanceof Function) {
          callback();
        }
        return;
      }
      let me = this;
      let script = document.createElement('script');
      script.type = 'text/javascript';
      script.onload = function () {
        me.jsCache[url.toString()] = true;
        if (callback instanceof Function) {
          callback();
        }

        document.getElementsByTagName('head')[0].removeChild(script);
      };
      script.src = url.toString();
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  }
  //将16进制的颜色转换为Vector3类型，Vector3的每一个值都在0-1之间
  public static hexColorToVector(colorStr: string): Vector3 {
    if (!colorStr) {
      return new Vector3(0, 0, 0);
    }

    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    // 把颜色值变成小写
    var color = colorStr.toLowerCase();
    var colorVector = new Vector3();
    if (reg.test(color)) {
      // 如果只有三位的值，需变成六位，如：#fff => #ffffff
      if (color.length === 4) {
        var colorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
        }
        color = colorNew;
      }
      // 处理六位的颜色值，转为RGB
      var colorChange = [];
      for (var i = 1; i < 7; i += 2) {
        colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
      }
      //   return "RGB(" + colorChange.join(",") + ")";

      colorVector.x = colorChange[0] / 255;
      colorVector.y = colorChange[1] / 255;
      colorVector.z = colorChange[2] / 255;
      return colorVector;
    } else {
      return colorVector;
    }
  }

  /**绕轴旋转 */
  public static rotateByAxisAngle(axis: Vector3, angle: number, vec: Vector3): Vector3 {
    var quaternion = new Quaternion();
    quaternion.setFromAxisAngle(axis, angle);
    vec.applyQuaternion(quaternion);

    return vec;
  }

  public static randomValue(min, max) {
    return min + max * (Math.random() - 0.5)
  }
  public static randomVector3(min, max) {
    const rand3 = new Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
    return new Vector3().addVectors(min, new Vector3().multiplyVectors(max, rand3))
  }
  public static getTileUrl(url: string, x: string, y: string, z: string, readableUrl: boolean = true) {
    if (!url) {
      return undefined;
    }
    else {
      if (readableUrl) {
        let realUrl = url.replace('[x]', x).replace('[y]', y).replace('[z]', z);

        realUrl = realUrl.replace('{x}', x).replace('{y}', y).replace('{z}', z);
        return realUrl;
      } else {
        let realUrl = url.replace('[x]', GeoUtils.getC16(parseInt(x), true))
          .replace('[y]', GeoUtils.getR16(parseInt(y), true))
          .replace('[z]', parseInt(z) < 9 ? 'L0' + z : 'L' + z);

        realUrl = realUrl.replace('{x}', GeoUtils.getC16(parseInt(x), true))
          .replace('{y}', GeoUtils.getR16(parseInt(y), true))
          .replace('{z}', parseInt(z) < 9 ? 'L0' + z : 'L' + z);
        return realUrl;
      }
    }

  }
  public static sphericalEquals(spherical: Spherical, spherical2: Spherical) {
    return (Math.abs(spherical2.radius - spherical.radius) < 0.01) &&
      (Math.abs(spherical2.phi - spherical.phi) < 0.001) &&
      (Math.abs(spherical2.theta - spherical.theta) < 0.001);
  }
  public static vector3Equals(vec1: Vector3, vec2: Vector3) {
    return (Math.abs(vec1.x - vec2.x) < 0.0001) &&
      (Math.abs(vec1.y - vec2.y) < 0.0001) &&
      (Math.abs(vec1.z - vec2.z) < 0.0001);
  }
  public static vector2Equals(vec1: Vector2, vec2: Vector2) {
    return (Math.abs(vec1.x - vec2.x) < 0.0001) &&
      (Math.abs(vec1.y - vec2.y) < 0.0001);
  }
  public static quaternionEquals(qua1: Quaternion, qua2: Quaternion) {
    return (Math.abs(qua1.x - qua2.x) < 0.00001) &&
      (Math.abs(qua1.y - qua2.y) < 0.00001) &&
      (Math.abs(qua1.z - qua2.z) < 0.00001) &&
      (Math.abs(qua1.w - qua2.w) < 0.00001);
  }


  /**
   * 判断坐标是否是经纬度（非精确判断）
   * @param x x坐标
   * @param y y坐标
   * @returns 
   */
  public static isLatLng(x, y) {
    return (-181 < x) && (x < 181) && (-181 < y) && (y < 181);
  }
  /**
   * 递归遍历对象
   * @param obj 要递归遍历的对象
   * @param callback 回调函数，返回false则不继续往下遍历
   * @returns 
   */
  public static traverseObject(obj, callback) {
    if (!obj || !obj.children || obj.children < 1) {
      return;
    }
    for (let i = 0; i < obj.children.length; i++) {
      if (!callback(obj.children[i]))
        continue;
      Helper.traverseObject(obj.children[i], callback);
    }
  }
  /**
 * 监听属性变化
 * @param target 要监听属性变化的对象
 * @param propertyName 要监听变化的属性
 * @param listener getter&setter
 */
  public static addPropertyListener(target, propertyName, listener: { getter: Function, setter: Function }) {
    const _this = target;
    if (Array.isArray(propertyName)) {
      for (let i = 0; i < propertyName.length; i++) {
        let prop = propertyName[i];
        _this[`_${prop}`] = _this[prop]
        Object.defineProperty(_this, prop, {
          get: function () {
            return listener.getter ? listener.getter(propertyName[i]) : _this[`_${prop}`];
          },
          set: function (newValue) {
            _this[`_${prop}`] = newValue
            if (listener.setter)
              listener.setter(propertyName[i], newValue)
          }
        })
      }
    }
    else {
      _this[`_${propertyName}`] = _this[propertyName]
      Object.defineProperty(_this, propertyName, {
        get: function () {
          return listener.getter ? listener.getter(propertyName) : _this[`_${propertyName}`];
        },
        set: function (newValue) {
          _this[`_${propertyName}`] = newValue
          if (listener.setter)
            listener.setter(newValue)
        }
      })
    }
  }

  //由近及远的访问瓦片
  public static visitTileFromNearToFar(tileRange: { minRow: number, maxRow: number, minCol: number, maxCol: number, zoom: number/*, centerRow: number, centerCol: number*/ }
    , cameraPositon: Vector3) {

    let nearestCol = GeoUtils.getC10(cameraPositon.x, tileRange.zoom);
    let nearestRow = GeoUtils.getR10(cameraPositon.y, tileRange.zoom);

    if (nearestCol < tileRange.minCol) {
      nearestCol = tileRange.minCol;
    }
    else if (nearestCol > tileRange.maxCol) {
      nearestCol = tileRange.maxCol;
    }
    if (nearestRow < tileRange.minRow) {
      nearestRow = tileRange.minRow;
    }
    else if (nearestRow > tileRange.maxRow) {
      nearestRow = tileRange.maxRow;
    }

    let inputArray = [];
    inputArray.push({ x: nearestCol, y: nearestRow, z: tileRange.zoom });
    // inputArray.push({x: tileRange.centerCol, y: tileRange.centerRow, z: tileRange.zoom});

    let resultArray = [];
    let resultSet = new Set();
    let firstTile: { x: number, y: number, z: number };

    while ((firstTile = inputArray.shift())) {
      let tileStr = JSON.stringify(firstTile);
      if (!resultSet.has(tileStr)) {
        resultArray.push(JSON.stringify(firstTile));
        resultSet.add(tileStr);

        let neighborArray = [];
        let upElement = { x: firstTile.x, y: firstTile.y + 1, z: tileRange.zoom };
        neighborArray.push(upElement);
        let downElement = { x: firstTile.x, y: firstTile.y - 1, z: tileRange.zoom };
        neighborArray.push(downElement);
        let leftElement = { x: firstTile.x - 1, y: firstTile.y, z: tileRange.zoom };
        neighborArray.push(leftElement);
        let rightElement = { x: firstTile.x + 1, y: firstTile.y, z: tileRange.zoom };
        neighborArray.push(rightElement);

        for (let i = 0; i < neighborArray.length; i++) {
          let element = neighborArray[i];
          if (element.x >= tileRange.minCol && element.x <= tileRange.maxCol && element.y >= tileRange.minRow && element.y <= tileRange.maxRow) {
            if (!resultSet.has(JSON.stringify(element))) {
              inputArray.push(element);
            }
          }
        }
      }
    }
    return resultArray;
  }

  /**
   * 计算一系列顶点的中心点
   * @param pointArray 顶点数组
   * @returns 中心点
   */
  public static getCenterPoint(pointArray) {
    let totalX = 0, totalY = 0, totalZ = 0;
    let minX, maxX, minY, maxY;
    let firstPoint = pointArray[0].toEPSGWeb();
    minX = firstPoint.x;
    maxX = firstPoint.x;
    minY = firstPoint.y;
    maxY = firstPoint.y;

    for (let i = 0; i < pointArray.length; i++) {
      let point = pointArray[i].toEPSGWeb();
      if (minX < point.x) {
        minX = point.x;
      } else if (maxX > point.x) {
        maxX = point.x;
      }

      if (minY < point.y) {
        minY = point.y;
      } else if (maxY > point.y) {
        maxY = point.y;
      }

      totalX += point.x;
      totalY += point.y;
      totalZ += point.z;
    }
    let length = pointArray.length;
    let centerPoint = new Point(totalX / length, totalY / length, totalZ / length, Const.EPSGType.EPSGWeb);
    let data = { centerPoint: centerPoint, minX: minX, maxX: maxX, minY: minY, maxY: maxY };
    return data;
  }
}
